<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .my-ripple {
        position: absolute;
        top: 0;
        left: 0;
        z-index: 100;
        border-radius: 50%;
        background-color: currentColor;
        opacity: 0;
        transition: transform 0.2s cubic-bezier(0.68, 0.01, 0.62, 0.6),
          opacity 0.08s linear;
        will-change: transform, opacity;
        pointer-events: none;
      }
      .btn {
        width: 100px;
        height: 50px;
      }
    </style>
  </head>
  <!-- 点击按钮水波纹效果 -->
  <body>
    <div class="btn">eee</div>
    <script>
      function computeRippleStyles(element, event) {
        const { top, left } = element.getBoundingClientRect();
        const { clientWidth, clientHeight } = element;

        const radius = Math.sqrt(clientWidth ** 2 + clientHeight ** 2) / 2;
        const size = radius * 2;

        const localX = event.clientX - left;
        const localY = event.clientY - top;

        const centerX = (clientWidth - radius * 2) / 2;
        const centerY = (clientHeight - radius * 2) / 2;

        const x = localX - radius;
        const y = localY - radius;

        return { x, y, centerX, centerY, size };
      }
      function createRipple(event) {
        const container = this;
        const { x, y, centerX, centerY, size } = computeRippleStyles(
          container,
          event
        );
        const ripple = document.createElement("div");
        ripple.classList.add("my-ripple");
        ripple.style.opacity = `0`;
        ripple.style.transform = `translate(${x}px, ${y}px) scale3d(.3, .3, .3)`;
        ripple.style.width = `${size}px`;
        ripple.style.height = `${size}px`;
        // 记录水波的创建时间
        ripple.dataset.createdAt = String(performance.now());

        const { position } = window.getComputedStyle(container);
        container.style.overflow = "hidden";
        position === "static" && (this.style.position = "relative");

        container.appendChild(ripple);

        window.setTimeout(() => {
          ripple.style.transform = `translate(${centerX}px, ${centerY}px) scale3d(1, 1, 1)`;
          ripple.style.opacity = `.25`;
        });
      }

      function removeRipple() {
        const container = this;
        const ripples = container.querySelectorAll(".my-ripple");
        if (!ripples.length) {
          return;
        }

        const lastRipple = ripples[ripples.length - 1];
        // 通过水波的创建时间计算出扩散动画还需要执行多久，确保每一个水波都完整的执行了扩散动画
        const delay =
          300 - performance.now() + Number(lastRipple.dataset.createdAt);

        setTimeout(() => {
          lastRipple.style.opacity = `0`;

          setTimeout(() => lastRipple.parentNode?.removeChild(lastRipple), 300);
        }, delay);
      }

      document
        .querySelector(".btn")
        .addEventListener("mousedown", createRipple);
      document.addEventListener("mouseup", removeRipple);
    </script>
  </body>
</html>
